要依賴注入,就不能只懂依賴注入
而依賴注入是在做什麼呢?讓協作類別,依賴於基礎設施,來獲取所需的服務
依賴注入本身不是目標,而是手段,使用手段的目的是為了寫出更好維護的程式
在不斷增長的程式碼中,為了使開發更有效率,我們會盡力使其易於維護,而最好的方式就是鬆耦合,針對抽象寫程式,而不是實作,鬆耦合的設計能提高擴展性,高擴展性也會提高維護性,而所謂依賴注入,也不過是提高維護性的手法
di 是模式、原則、設計,但他不是 container or library,所謂 library 是定義了依賴注入方法的套件庫,讓開發者可以更輕鬆的實踐依賴注入,但他並非必要的,在不使用套件的情況下做的依賴注入稱之為簡單依賴注入
鬆耦合的架構,可以讓程式更易於開發、維護、擴展、測試,而核心就是針對介面做設計,而不是實體
詳細的方法,在 Day 11 ~ Day 24 全都是可以用到的技巧
既然已經針對介面做出設計,也知道物件要依賴於抽象而非實體,那就剩下一個問題,拿如何獲得?
答案就是今天的主題 依賴注入
在 stackoverflow 裡面有一串How to explain dependency injection to a 5-year-old? [closed]講到
When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.
What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.
簡單的翻譯後就是,小朋友應該說出需求,而爸比或媽咪會去完成,像是你說你需要飲料配午餐,你就只需要坐著就好,爸比媽咪會確保你有飲料喝
可以看到,透過依賴注入,我們可以將物件的建立,和物件的使用,兩種不同的責任分離,進一步減少物件的職責,也讓物件的內聚性更高
inversion of control
依賴抽象,透過抽象去操作實際物件,是控制依賴關係的方向
dependency Inversion Principle
讓實作依賴於抽象,而不是抽象依賴於實作,是處理依賴關係的模式
抽象可以說是物件導向概念裡面最重要的一點,我會用餅乾模具來描述抽象
抽象就像是餅乾模具,而物件實體的定義就是我們壓出來的餅乾則是實體,抽象定義了小草的形狀,而實作的奶油餅乾、紅絲絨餅乾都會是小草的形狀,而當我們販售小草造型餅乾時,抹茶、奶油、紅絲絨三種餅乾都可以混賣
那回到這篇主題,為什麼抽象在這邊重要,我們不僅是讓物件依賴於實體、以介面去設計,定義依賴注入時才能讓我們替換不同的實體類別,當然以介面的觀點進行開發並不是為所有類別設計介面,那些不需要被模擬、介入攔截、替換的類別,就不需要將類別隱藏於介面之後
依賴注入框架會定義類別建構方法,而在某些環境中,建構方法根據設計模式也會有所不同,導致開發者需要先理解設計模式、使用方法並瞭解依賴注入框架,舉個例子吧
// 要求使用建構者模式需要使用 provide
@Provide
fun provideRepository():Retrofit {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
// 回傳介面時用 bind
@Binds
fun bindsRepository():Repository {
}
優點
缺點
服務定位器被稱為是反模式
Since Koin isn’t a dependency injector but a service locator with a clever reified trick that you can use to manually perform dependency injection, the boilerplate will scale disproportionally — said Jake Wharton.
服務定位與依賴注入最大的差異在於,他無法通過注入完全物件建構,我們必須手動傳入綁定
single { Controller(get()) }
另一方面,服務定位器會在運行時動態建立物件實體,換句話說,他也會在運行時崩潰,而真正的依賴注入不同,他會產出程式碼,並在編譯時自動檢查依賴關係,如果沒有正確的提供依賴關係,我們便無法建構應用程序,就是說,他可以保證應運運行時不會有依賴關係的問題發生
依賴注入:原理、實作與設計模式
Dependency Injection Principles, Practices, and Patterns 1st Edition
這本書我也是正在看,文章分享比起實作更重要的設計概念,如有錯誤歡迎指正
Rather than other design pattern, di usually connect to other design pattern, so it would be better that you dive into di after you have basic knowledge of design pattern
So what does di doing? it allow the cooperator class, rely on basic instrument, to get what it needs
The di it self is not our goal, it is a solution, a solution for program which easier to maintain
In our growing program, we do everything we can to make it more maintainable, the best solution is decoupled, we write code based on abstract, not based on instance, the design of decouple could lead us to increase extendability, maintainability, the whole thing about di, is a solution to increase maintainability.
dependency inject is a pattern, principle, design, it is not just container or library
, the library is a code base implement the idea of di, so we developer could use it simpler, but we of course could use di without it, a simple way if called simple dependency inject
The architecture of decoupled, allow the program easier to develop, maintain, extend, test, the core is rely on abstract, not instance
you can check out articles in this series, from day 11 to day 24
now we know we should design for abstract, and we also know that we rely on abstract not implement, so the last question is how do we get the constructor?
And that is our topic today, dependency inject
There is a discussion in stackoverflow, How to explain dependency injection to a 5-year-old? [closed] mentioned
When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired.
What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.
as we can see, by using di, we could separate the responsibility of object create and object usage, decrease the responsibility of object and increase the cohesions
inversion of control
dependency Inversion Principle
abstract is the more important point in oop, we will use cookie mold to describe it
abstract is like a cookie mold, the instance of class it the cookie dough we make, the abstract define the shape, and the instance butter cookie, red velvet cookie will the same shape of abstract, so when we selling grass shape cookie, we can mix the different cookie that are same shape
and back to the topic, why does abstract matter, we are not just make class rely on instance, design based on interface, when we define the dependency inject using abstract allow us to replace instance, on the other hands, not all class require an abstract, those class doesn't require mock, intercept, replace, doesn't require hiding class behind interface
as we building instance with di, di would know the construct way of the class, and base on environment, we might have different usage depends on the class design, requires developer know about other design pattern, usage and di framework, we can take an example
// ask developer using `provide` with builder pattern
@Provide
fun provideRepository():Retrofit {
return Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(AnalyticsService::class.java)
}
// using bind to return `interface`
@Binds
fun bindsRepository():Repository {
}
pros
cons
some developer declare that service locator is an anti pattern
Since Koin isn’t a dependency injector but a service locator with a clever reified trick that you can use to manually perform dependency injection, the boilerplate will scale disproportionally — said Jake Wharton.
The biggest difference between service locator and di, it can't inject class automatically, we have to bind it manually
single { Controller(get()) }
On the other hands, service locator will create class instance in runtime, in other words, it will crash in runtime as well, unlike to real di, di will auto generate code, and auto check dependency system, if we didn't provide right dependency relationship, we can't build our app, which means we guarantee we won;t have dependency in run time
Dependency Injection Principles, Practices, and Patterns 1st Edition
Great book, this article mostly is concept from that book, but only contain a small piece